﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace PetriNetSharp.Engine
{
	/// <summary>
	/// A logical Arc in a petri net
	/// </summary>
	public class Arc : Iinvalidatable
	{

		/// <summary>
		/// The PetriObject at the start of this Arc
		/// </summary>
		private PetriObject start;

		/// <summary>
		/// The PetriObject at the end of this Arc
		/// </summary>
		private PetriObject end;


		/// <summary>
		/// Create a new arc
		/// </summary>
		/// <param name="start">The PetriObject this Arc starts at</param>
		/// <param name="end">The PetriObject this Arc ends at</param>
		/// <preconditions>
		/// Start and end must not be of the same type
		/// <example>
		/// Start and End cannot both be of type <code>Transition</code>
		/// </example>
		/// </preconditions>
		public Arc(PetriObject start, PetriObject end)
		{
			#region preconditions
			Debug.Assert(!start.GetType().Equals(end.GetType()), 
				"Start and End cannot be the same type",
				"An arc was constructed with start and end being the same type");
			#endregion

			this.start = start;
			this.end = end;

			start.Invalidated += Invalidate;
			end.Invalidated += Invalidate;
		}


		/// <summary>
		/// Get this starting point of this Arc
		/// </summary>
		public System.Windows.Point StartingPoint { get { return start.Location; } }

		/// <summary>
		/// Get this ending point of this Arc
		/// </summary>
		public System.Windows.Point EndingPoint { get { return end.Location; } }

		/// <summary>
		/// Get the start PetriObject of this Arc
		/// </summary>
		public PetriObject Start { get { return start; } }

		/// <summary>
		/// Get the end PetriObject of this Arc
		/// </summary>
		public PetriObject End { get { return end; } }


		/// <summary>
		/// Get the Place this Arc is connected to
		/// </summary>
		public Place Place
		{
			get
			{
				if ( this.IsInputArc )
					return (Place)start;
				else
					return (Place)end;
			}
		}

		/// <summary>
		/// Get the Transition this Arc is connected to
		/// </summary>
		public Transition Transition
		{
			get
			{
				if ( this.IsInputArc )
					return (Transition)end;
				else
					return (Transition)start;
			}
		}

		/// <summary>
		/// Get whether this Arc is an input Arc (starts at a 
		/// Place and goes into a Transition)
		/// </summary>
		public Boolean IsInputArc { get { return (start is Place); } }

		/// <summary>
		/// Get whether this Arc is an output Arc (starts at a
		/// Transition and goes into a Place)
		/// </summary>
		public Boolean IsOutputArc { get { return !IsInputArc; } }


		#region Object method overrides

		/// <summary>
		/// Determines whether the object passed is equal to this arc
		/// </summary>
		/// <param name="obj">The object to compare to</param>
		/// <returns>True if they are equal, false if not</returns>
		public override bool Equals(object obj)
		{
			if (obj is Arc)
			{
				Arc inArc = obj as Arc;

				if (inArc.Place.Equals(Place) && inArc.Transition.Equals(Transition))
					return true;

				return false;
			}
			return base.Equals(obj);
		}

		/// <summary>
		/// Serves as a hash function for this type
		/// </summary>
		/// <returns>A hash code for the current Arc</returns>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		#endregion


		#region Iinvalidatable Members

		/// <summary>
		/// Occurs when this Arc is invalidated
		/// </summary>
		public event Action Invalidated;

		/// <summary>
		/// Invalidate this Arc
		/// </summary>
		public void Invalidate()
		{
			if (Invalidated != null)
				Invalidated();
		}

		#endregion
	}
}
